package _effx.greensock
{
   import com.greensock.TweenLite;
   import com.greensock.plugins.TweenPlugin;
   
   import flash.display.BitmapData;
   import flash.display.Sprite;
   import flash.events.Event;
   import flash.geom.Matrix;
   import flash.geom.Point;
   import flash.geom.Rectangle;
   import flash.utils.Dictionary;

   /**
    * @author  cjboy | cjboy1984@gmail.com
    * @version Apr,14,2011
    * @link    http://wonderfl.net/c/qnTR/
    * @usage
    *
    *    - TODO : 想個辦法完成
    *
    * // !!! RESTRICTION !!!
    * NOT sure if it works fine with scaleX and scaleY  - TODO : 克服scaleX & scaleY的issue.
    * Target can be a MovieClip object, but it just render the first screen of the object.
    *
    * TweenPlugin.activate([GinnyPlugin]);
    *
    * TweenMax.to(mc, 1, { ginny:{vaninshingPoint:new Point(100, 100), hide:true, autoDestroy:false} } );
    * TweenMax.to(mc, 1, { delay:1, ginny:{vaninshingPoint:new Point(100, 100), hide:false} } );
    */
   public class GinnyPlugin extends TweenPlugin
   {
      private const BMP_NAME:String = '_gs_ginny_';
      private const SEGMENT:int = 50;

      public static const VERSION:Number = 1.0;
      public static const API:Number = 1.0;

      // target
      private var ta:Sprite;
      private var taWidth:Number;
      private var taHeight:Number;
      // The effect bitmap.
      private var bmp:Sprite;
      private var bmpData:BitmapData;
      // vertex
      private var vertexs:Array;
      // user given properties.
      private var vanishingPoint:Point;
      private var hide:Boolean;
      private var autoDestroy:Boolean;

      // dictionaries
      private static var _nameDict:Dictionary = new Dictionary();

      public function GinnyPlugin()
      {
         super();
         this.propName = "ginny";
         this.overwriteProps = ["ginny"];
         this.onComplete = onCompleteTween;
      }

      // ------------------ LINE -------------------

      override public function onInitTween(pTarget:Object, pValue:*, tween:TweenLite):Boolean
      {
         if (!(pTarget is Sprite))
         {
            trace("[Err] GreenSock-GinnyPlugin : It works only for Sprite");
            return false;
         }
         else if (typeof(pValue) != "object")
         {
            trace("[Err] GreenSock-GinnyPlugin : Parameter must be a object");
            return false;
         }

         ta = pTarget as Sprite;
         if (!ta.parent)
         {
            trace("[Err] GreenSock-GinnyPlugin : [" + ta.name + "]'s parent is undefined!");
            return false;
         }
         taWidth = ta.width;
         taHeight = ta.height;

         // init properties.
         vanishingPoint = (pValue.vanishingPoint == undefined) ? new Point(0, 0) : pValue.vanishingPoint;
         hide = (pValue.hide == undefined) ? false : pValue.hide;
         autoDestroy = (pValue.autoDestroy == undefined) ? true : pValue.autoDestroy;

         // find name from dictionary.
         var nname:String;
         if (_nameDict[ta] == undefined)
         {
            nname = BMP_NAME + ta.name;
            _nameDict[ta] = nname;
         }
         else
         {
            nname = _nameDict[ta] as String;
         }

         // find bmp from name.
         bmp = ta.parent.getChildByName(nname) as Sprite;
         if (bmp == null)
         {
            bmp = new Sprite();
            bmp.name = nname;
         }
         var taIndex:int = ta.parent.getChildIndex(ta);
         ta.parent.addChildAt(bmp, taIndex);
         ta.visible = false;

         // render
         draw();

         // remove effect if target is removed from stage.
         ta.addEventListener(Event.REMOVED_FROM_STAGE, onRemove);

         return true;
      }

      // ------------------ LINE -------------------

      override public function get changeFactor():Number { return super.changeFactor; }
      override public function set changeFactor(value:Number):void
      {
         super.changeFactor = value;

         var xx:int, yy:int, delay:Number;
         var px:Number = SEGMENT * (vanishingPoint.x / taWidth);
         var py:Number = SEGMENT * (vanishingPoint.y / taHeight);
      }

      // ------------------ LINE -------------------

      override public function killProps($lookup:Object):void
      {
         super.killProps($lookup);
         autoDestroy = true;
         onCompleteTween();
      }

      // ################ Protected ################

      // ################# Private #################

      private function draw():void
      {
         // get boundary rectangle relative to the coordinate system of ta.parent
         var taParentBound:Rectangle = ta.getBounds(ta.parent);

         // get boundary rectangle relative to the coordinate system of ta
         var taBound:Rectangle = ta.getBounds(ta);

         // cache target as BitmapData.
         var mtx:Matrix = new Matrix();
         mtx.translate( -taBound.x, -taBound.y);
         mtx.scale(ta.scaleX, ta.scaleY);
         bmpData = new BitmapData(taWidth, taHeight, true, 0x00000000);
         bmpData.draw(ta, mtx);

         // calculate triangles.
         var vertices:Vector.<Number> = new Vector.<Number>();
         var indices:Vector.<int> = new Vector.<int>();
         var uvtData:Vector.<Number> = new Vector.<Number>();

         vertexs = [];
         for (var xx:int = 0; xx < SEGMENT; xx++)
         {
            vertexs[xx] = [];
            for (var yy:int = 0; yy < SEGMENT; yy++)
            {
               vertexs[xx][yy] = new Point(xx * taWidth / SEGMENT, yy * taHeight / SEGMENT);

               vertices[vertices.length] = vertexs[xx][yy].x
               vertices[vertices.length] = vertexs[xx][yy].y
               uvtData[uvtData.length] = xx / SEGMENT;
               uvtData[uvtData.length] = yy / SEGMENT;
            }
         }

         for (var i:int = 0; i < SEGMENT - 1; i++)
         {
            for (var j:int = 0; j < SEGMENT - 1; j++)
            {
               indices.push(i * SEGMENT + j, i * SEGMENT + j + 1, (i + 1) * SEGMENT + j);
               indices.push(i * SEGMENT + j + 1, (i + 1) * SEGMENT + 1 + j, (i + 1) * SEGMENT + j);
            }
         }

         bmp.x = taParentBound.x;
         bmp.y = taParentBound.y;
         bmp.graphics.clear();
         bmp.graphics.beginBitmapFill(bmpData);
         bmp.graphics.drawTriangles(vertices, indices, uvtData);
         bmp.graphics.endFill();
      }

      // ------------------ LINE -------------------

      private function onCompleteTween():void
      {
         // check whether to keep the JellyQ effect or remove it.
         if (autoDestroy == false) return;

         ta.visible = true;

         // destory bmpData
         bmpData.dispose();
         bmpData = null;

         // remove effect bmp.
         if (ta.parent && ta.parent.contains(bmp))
         {
            ta.parent.removeChild(bmp);
         }

         // destroy effect bmp.
         bmp.graphics.clear();
         bmp = null;
      }

      // ------------------ LINE -------------------

      /**
       * Prevent the target from being removed from stage without killing its tween.
       */
      private function onRemove(e:Event):void
      {
         ta.removeEventListener(Event.REMOVED_FROM_STAGE, onRemove);

         autoDestroy = true;
         onCompleteTween();
      }

      // ------------------ LINE -------------------

   }

}